/*
*
==============================================================================
*  Name        : 
*  Part of     : Omen Game Engine
*  Description : 
*  Version     : 1.0
*
*  Copyright (c) 2013 Lauri Kortevaara.
* 
==============================================================================
*/

#define OMEN_SOURCE 1

/// Omen Headers
#include "OmenAssetManager.h"
#include "OmenFileUtils.h"

using namespace Omen;


/// STL Headers
#include <algorithm>
#include <string>
#include <istream>
#include <vector>
#include <iostream>

Asset::Asset()
    : pTreeNode(NULL),assetType(Omen::Asset_TypeUnkown), assetFileName(L""), assetPath(L"") 
{
}

void Asset::setType( Omen::AssetType type ) 
{ 
    assetType = type; 
}

Omen::AssetType& Asset::type() { return assetType; }               // Returns a reference to the asset type
        
std::wstring& Asset::path() { return assetPath; }            /// Returns the path of the asset
std::wstring& Asset::filename() { return assetFileName; }        /// Returns the filename of the asset

ModelAsset::ModelAsset( const std::wstring& name  ) 
{ 
    m_name = name;
    Asset::setType(Omen::Asset_3dModel); 
}

ModelAsset::~ModelAsset()
{
    releaseAsset();
}

/// Loads the asset and return true if succeeded, false otherwise
bool ModelAsset::loadAsset()
{
    return false;
}

/// Releases the allocated resources by this asset
bool ModelAsset::releaseAsset()
{
    return false;
}

TextureMapAsset::TextureMapAsset( const std::wstring& name  ) 
{ 
    m_name = name;
    Asset::setType(Omen::Asset_TextureMap); 
}

TextureMapAsset::~TextureMapAsset()
{
}

/// Loads the asset and return true if succeeded, false otherwise
bool TextureMapAsset::loadAsset()
{
    return false;
}

/// Releases the allocated resources by this asset
bool TextureMapAsset::releaseAsset()
{
    return false;
}
   

AudioAsset::AudioAsset( const std::wstring& name  ) 
{ 
    m_name = name;
    Asset::setType(Omen::Asset_Audio); 
}

AudioAsset::~AudioAsset()
{
}
/// Loads the asset and return true if succeeded, false otherwise
bool AudioAsset::loadAsset()
{
    return false;
}

/// Releases the allocated resources by this asset
bool AudioAsset::releaseAsset()
{
    return false;
}

ScriptAsset::ScriptAsset( const std::wstring& name  ) 
{ 
    m_name = name;
    Asset::setType(Omen::Asset_Script); 
}

ScriptAsset::~ScriptAsset()
{
}
/// Loads the asset and return true if succeeded, false otherwise
bool ScriptAsset::loadAsset()
{
    return false;
}

/// Releases the allocated resources by this asset
bool ScriptAsset::releaseAsset()
{
    return false;
}

AnimationAsset::AnimationAsset( const std::wstring& name  ) 
{ 
    m_name = name;
    Asset::setType(Omen::Asset_Animation); 
}

AnimationAsset::~AnimationAsset()
{
}

/// Loads the asset and return true if succeeded, false otherwise
bool AnimationAsset::loadAsset()
{
    return false;
}

/// Loads the asset and return true if succeeded, false otherwise
bool AnimationAsset::releaseAsset()
{
    return false;
}

MovieAsset::MovieAsset( const std::wstring& name  ) 
{ 
    m_name = name;
    Asset::setType(Omen::Asset_Movie); 
}

MovieAsset::~MovieAsset()
{
}

/// Loads the asset and return true if succeeded, false otherwise
bool MovieAsset::loadAsset()
{
    return false;
}

/// Releases the allocated resources by this asset
bool MovieAsset::releaseAsset()
{
    return false;
}

/// Adds the given node as a child
void AssetNode::addChild( IAssetNode* pChild )
{
    m_listChildren.push_back( pChild );
}

std::wstring AssetNode::name() const
{
    return m_name;
}

IAsset* AssetNode::asset()
{
    return m_pAsset;
}

void AssetNode::setAsset( IAsset* pAsset )
{
    m_pAsset = pAsset;
}

unsigned long AssetNode::childCount() const
{
    return m_listChildren.size();
}

/// Loads the asset and return true if succeeded, false otherwise
bool AssetNode::loadAsset()
{
    return false;
}

/// Releases the allocated resources by this asset
bool AssetNode::releaseAsset()
{
    return false;
}

/// Returns the children of this node
unsigned long AssetNode::getChildren( IAssetNode** children, unsigned long& maxNum )
{
    unsigned long count = 0;
    if( children )
    {
        count = maxNum = std::min<unsigned long>( m_listChildren.size(), maxNum );
        for( unsigned long i = 0; i < maxNum; ++i ) children[i] = m_listChildren.at(i);
    }
    return count;
}

/// Searches through child nodes and returns a matching node if found, NULL otherwise
IAssetNode* AssetNode::findChild( const std::wstring& nodeName ) const
{
    std::vector< IAssetNode* >::const_iterator iter = m_listChildren.begin();
    
    // First go through own children
    if( !this->m_listChildren.empty() )
    {
        while( true )
        {
            if( (*iter) && (*iter)->name().find(nodeName.c_str(), 0, nodeName.length()) != std::wstring::npos )
                return *iter;
            ++iter;
            if( iter == m_listChildren.end() )
                break;
        }
        // And then if we didn't find the matching child, go through child nodes
        iter = m_listChildren.begin();
        while( true )
        {
            if( (*iter) )
            {
                IAssetNode* pChildNode = (*iter)->findChild(nodeName);
                if (pChildNode)
                    return pChildNode;
            }
            ++iter;
            if( iter == m_listChildren.end() )
                break;
        }
    }
    return NULL;
}

AssetManager::AssetManager( Omen::IObject* pParent ) 
{
    // Omen::IObject
    this->setParent(pParent);

    // Create root asset node
    m_rootAssetNode = new AssetNode( NULL, L"Assets" );

    // Add default asset container nodes
    m_rootAssetNode->addChild( new AssetNode( m_rootAssetNode, L"3d Models" ) );
    m_rootAssetNode->addChild( new AssetNode( m_rootAssetNode, L"Texture Maps" ) );
    m_rootAssetNode->addChild( new AssetNode( m_rootAssetNode, L"Audio" ) );
    m_rootAssetNode->addChild( new AssetNode( m_rootAssetNode, L"Scripts" ) );
    m_rootAssetNode->addChild( new AssetNode( m_rootAssetNode, L"Animations" ) );
    m_rootAssetNode->addChild( new AssetNode( m_rootAssetNode, L"Movies" ) );
}

/// Enumerate assets with given type in given directory and adds them to the asset manager
#ifdef _WIN32

unsigned long AssetManager::enumerateAssetsByExtAndType( const Omen::AssetType& type, 
                                                         std::vector<std::wstring>& assetExts, 
                                                         const std::wstring& assetPath, 
                                                         const std::wstring& assetNodeName,
                                                         bool recursive )
{
    unsigned long numFoundAssets = 0;
    Omen::FileUtils fileUtils;
    std::vector<std::wstring> filesFound;

    // Start finding the given asset files
    while( !assetExts.empty() )
    {
        std::wstring assetFileName = assetExts.back();
        assetExts.pop_back();

        if( fileUtils.findFiles( assetPath, assetFileName, recursive ) )
        {
            std::wstring* fileNames = new std::wstring[fileUtils.filesFoundCount()];
            fileUtils.getFoundFiles(fileNames, fileUtils.filesFoundCount());
            for( unsigned long i = 0; i < fileUtils.filesFoundCount(); ++i )
                filesFound.push_back( fileNames[i] );
            delete[] fileNames;
        }
    }
        
    numFoundAssets = filesFound.size();
    IAssetNode* pAssets = this->m_rootAssetNode->findChild(assetNodeName);
    // create asset objects if any files were found.
    if( pAssets )
    while( !filesFound.empty() )
    {
        std::wstring filePath = filesFound.back();
        filesFound.pop_back();
        
        std::replace(filePath.begin(), filePath.end(), L'\\', L'/' );
        std::wstring filename = filePath.substr( filePath.find_last_of( L'/' )+1 );
        filePath = filePath.substr( 0, filePath.find_last_of( L'/' )+1 );
        
        IAssetNode* pNode  = new AssetNode( pAssets, filename );
        IAsset* pAsset = NULL;
        switch( type )
        {
            case Omen::Asset_TextureMap : pAsset = new TextureMapAsset(filename); break;
            case Omen::Asset_3dModel : pAsset = new ModelAsset(filename); break;
            case Omen::Asset_Audio : pAsset = new AudioAsset(filename); break;
            case Omen::Asset_Animation : pAsset = new AnimationAsset(filename); break;
            case Omen::Asset_Script : pAsset = new ScriptAsset(filename); break;
            case Omen::Asset_Movie : pAsset = new MovieAsset(filename); break;
            default:
                break;
        }
        if( pAsset )
        {
            pAsset->filename() = filename;
            pAsset->path() = filePath;
            pNode->setAsset(pAsset);
            pAssets->addChild(pNode);
            std::wcout << L"Added new Asset: " << filePath << filename << std::endl;
        }
    }
    return numFoundAssets;
}

unsigned long AssetManager::enumerateAssets( const Omen::AssetType& type, const std::wstring& assetPath, bool recursive )
{
    unsigned long numFoundAssets = 0;
    switch( type )
    {
        case Omen::Asset_TextureMap:
        {
            std::vector<std::wstring> fileTypes;

            fileTypes.push_back( L"*.jpg" );
            fileTypes.push_back( L"*.jpeg" );
            fileTypes.push_back( L"*.png" );
            //fileTypes.push_back( L"*.tga" );
            fileTypes.push_back( L"*.dds" );
            
            numFoundAssets = enumerateAssetsByExtAndType( type, fileTypes, assetPath, L"Texture Maps", recursive );
            break;
        }
        case Omen::Asset_3dModel:
        {
            std::vector<std::wstring> fileTypes;

            fileTypes.push_back( L"*.omf" );
            fileTypes.push_back( L"*.3ds" );

            numFoundAssets = enumerateAssetsByExtAndType( type, fileTypes, assetPath, L"3d Models", recursive );
            break;
        }
    }
    return numFoundAssets;
}
#elif defined(__APPLE__) && defined(__MACH__)
unsigned long AssetManager::enumerateAssets( const Omen::AssetType& type, const std::wstring& assetPath, bool recursive )
{

}
#endif

/// Get anumerated assets of given type
void AssetManager::getAssets( const Omen::AssetType& type, std::vector<IAssetNode>& assets )
{
    
}

/// Get the root node of all assets
void AssetManager::getAssets( IAssetNode& rootNode )
{
    
}

/// Find an asset with given type and name that exactly or partially matches the given name
bool AssetManager::findAssets( const Omen::AssetType& type, const std::wstring& name, std::vector<IAssetNode*>& assets )
{
    bool bFound = false;
    
    std::wstring typeRootName;

    switch( type )
    {
        case Omen::Asset_TextureMap:
        {
            typeRootName = L"Texture Maps";
            break;
        }
        case Omen::Asset_3dModel:
        {
            typeRootName = L"3d Models";
            break;
        }
    }

    IAssetNode* pAssets = this->m_rootAssetNode->findChild( typeRootName );
    if( pAssets )
    {
        std::wstring assetName = name;
        if( assetName.find( L".tga" ) != std::wstring::npos )
        assetName.replace( assetName.find( L".tga" ), 
                  assetName.find( L".tga" )+4, 
                  L".jpg" );
        IAssetNode* pAssetNode = pAssets->findChild( assetName );
        if( pAssetNode )
        {
            bFound = true;
            assets.push_back( pAssetNode );
        }
    }
    
    return bFound;
}

/// EOF